home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / dbus / service.py < prev    next >
Text File  |  2009-05-06  |  35KB  |  830 lines

  1. # Copyright (C) 2003-2006 Red Hat Inc. <http://www.redhat.com/>
  2. # Copyright (C) 2003 David Zeuthen
  3. # Copyright (C) 2004 Rob Taylor
  4. # Copyright (C) 2005-2006 Collabora Ltd. <http://www.collabora.co.uk/>
  5. #
  6. # Permission is hereby granted, free of charge, to any person
  7. # obtaining a copy of this software and associated documentation
  8. # files (the "Software"), to deal in the Software without
  9. # restriction, including without limitation the rights to use, copy,
  10. # modify, merge, publish, distribute, sublicense, and/or sell copies
  11. # of the Software, and to permit persons to whom the Software is
  12. # furnished to do so, subject to the following conditions:
  13. #
  14. # The above copyright notice and this permission notice shall be
  15. # included in all copies or substantial portions of the Software.
  16. #
  17. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. # DEALINGS IN THE SOFTWARE.
  25.  
  26. __all__ = ('BusName', 'Object', 'method', 'signal')
  27. __docformat__ = 'restructuredtext'
  28.  
  29. import sys
  30. import logging
  31. import operator
  32. import traceback
  33. try:
  34.     import thread
  35. except ImportError:
  36.     import dummy_thread as thread
  37.  
  38. import _dbus_bindings
  39. from dbus import SessionBus, Signature, Struct, validate_bus_name, \
  40.                  validate_object_path, INTROSPECTABLE_IFACE, ObjectPath
  41. from dbus.decorators import method, signal
  42. from dbus.exceptions import DBusException, \
  43.                             NameExistsException, \
  44.                             UnknownMethodException
  45. from dbus.lowlevel import ErrorMessage, MethodReturnMessage, MethodCallMessage
  46. from dbus.proxies import LOCAL_PATH
  47.  
  48.  
  49. _logger = logging.getLogger('dbus.service')
  50.  
  51.  
  52. class _VariantSignature(object):
  53.     """A fake method signature which, when iterated, yields an endless stream
  54.     of 'v' characters representing variants (handy with zip()).
  55.  
  56.     It has no string representation.
  57.     """
  58.     def __iter__(self):
  59.         """Return self."""
  60.         return self
  61.  
  62.     def next(self):
  63.         """Return 'v' whenever called."""
  64.         return 'v'
  65.  
  66. class BusName(object):
  67.     """A base class for exporting your own Named Services across the Bus.
  68.  
  69.     When instantiated, objects of this class attempt to claim the given
  70.     well-known name on the given bus for the current process. The name is
  71.     released when the BusName object becomes unreferenced.
  72.  
  73.     If a well-known name is requested multiple times, multiple references
  74.     to the same BusName object will be returned.
  75.  
  76.     Caveats
  77.     -------
  78.     - Assumes that named services are only ever requested using this class -
  79.       if you request names from the bus directly, confusion may occur.
  80.     - Does not handle queueing.
  81.     """
  82.     def __new__(cls, name, bus=None, allow_replacement=False , replace_existing=False, do_not_queue=False):
  83.         """Constructor, which may either return an existing cached object
  84.         or a new object.
  85.  
  86.         :Parameters:
  87.             `name` : str
  88.                 The well-known name to be advertised
  89.             `bus` : dbus.Bus
  90.                 A Bus on which this service will be advertised.
  91.  
  92.                 Omitting this parameter or setting it to None has been
  93.                 deprecated since version 0.82.1. For backwards compatibility,
  94.                 if this is done, the global shared connection to the session
  95.                 bus will be used.
  96.  
  97.             `allow_replacement` : bool
  98.                 If True, other processes trying to claim the same well-known
  99.                 name will take precedence over this one.
  100.             `replace_existing` : bool
  101.                 If True, this process can take over the well-known name
  102.                 from other processes already holding it.
  103.             `do_not_queue` : bool
  104.                 If True, this service will not be placed in the queue of
  105.                 services waiting for the requested name if another service
  106.                 already holds it.
  107.         """
  108.         validate_bus_name(name, allow_well_known=True, allow_unique=False)
  109.  
  110.         # if necessary, get default bus (deprecated)
  111.         if bus is None:
  112.             import warnings
  113.             warnings.warn('Omitting the "bus" parameter to '
  114.                           'dbus.service.BusName.__init__ is deprecated',
  115.                           DeprecationWarning, stacklevel=2)
  116.             bus = SessionBus()
  117.  
  118.         # see if this name is already defined, return it if so
  119.         # FIXME: accessing internals of Bus
  120.         if name in bus._bus_names:
  121.             return bus._bus_names[name]
  122.  
  123.         # otherwise register the name
  124.         name_flags = (
  125.             (allow_replacement and _dbus_bindings.NAME_FLAG_ALLOW_REPLACEMENT or 0) |
  126.             (replace_existing and _dbus_bindings.NAME_FLAG_REPLACE_EXISTING or 0) |
  127.             (do_not_queue and _dbus_bindings.NAME_FLAG_DO_NOT_QUEUE or 0))
  128.  
  129.         retval = bus.request_name(name, name_flags)
  130.  
  131.         # TODO: more intelligent tracking of bus name states?
  132.         if retval == _dbus_bindings.REQUEST_NAME_REPLY_PRIMARY_OWNER:
  133.             pass
  134.         elif retval == _dbus_bindings.REQUEST_NAME_REPLY_IN_QUEUE:
  135.             # queueing can happen by default, maybe we should
  136.             # track this better or let the user know if they're
  137.             # queued or not?
  138.             pass
  139.         elif retval == _dbus_bindings.REQUEST_NAME_REPLY_EXISTS:
  140.             raise NameExistsException(name)
  141.         elif retval == _dbus_bindings.REQUEST_NAME_REPLY_ALREADY_OWNER:
  142.             # if this is a shared bus which is being used by someone
  143.             # else in this process, this can happen legitimately
  144.             pass
  145.         else:
  146.             raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name, retval))
  147.  
  148.         # and create the object
  149.         bus_name = object.__new__(cls)
  150.         bus_name._bus = bus
  151.         bus_name._name = name
  152.  
  153.         # cache instance (weak ref only)
  154.         # FIXME: accessing Bus internals again
  155.         bus._bus_names[name] = bus_name
  156.  
  157.         return bus_name
  158.  
  159.     # do nothing because this is called whether or not the bus name
  160.     # object was retrieved from the cache or created new
  161.     def __init__(self, *args, **keywords):
  162.         pass
  163.  
  164.     # we can delete the low-level name here because these objects
  165.     # are guaranteed to exist only once for each bus name
  166.     def __del__(self):
  167.         self._bus.release_name(self._name)
  168.         pass
  169.  
  170.     def get_bus(self):
  171.         """Get the Bus this Service is on"""
  172.         return self._bus
  173.  
  174.     def get_name(self):
  175.         """Get the name of this service"""
  176.         return self._name
  177.  
  178.     def __repr__(self):
  179.         return '<dbus.service.BusName %s on %r at %#x>' % (self._name, self._bus, id(self))
  180.     __str__ = __repr__
  181.  
  182.  
  183. def _method_lookup(self, method_name, dbus_interface):
  184.     """Walks the Python MRO of the given class to find the method to invoke.
  185.  
  186.     Returns two methods, the one to call, and the one it inherits from which
  187.     defines its D-Bus interface name, signature, and attributes.
  188.     """
  189.     parent_method = None
  190.     candidate_class = None
  191.     successful = False
  192.  
  193.     # split up the cases when we do and don't have an interface because the
  194.     # latter is much simpler
  195.     if dbus_interface:
  196.         # search through the class hierarchy in python MRO order
  197.         for cls in self.__class__.__mro__:
  198.             # if we haven't got a candidate class yet, and we find a class with a
  199.             # suitably named member, save this as a candidate class
  200.             if (not candidate_class and method_name in cls.__dict__):
  201.                 if ("_dbus_is_method" in cls.__dict__[method_name].__dict__
  202.                     and "_dbus_interface" in cls.__dict__[method_name].__dict__):
  203.                     # however if it is annotated for a different interface
  204.                     # than we are looking for, it cannot be a candidate
  205.                     if cls.__dict__[method_name]._dbus_interface == dbus_interface:
  206.                         candidate_class = cls
  207.                         parent_method = cls.__dict__[method_name]
  208.                         successful = True
  209.                         break
  210.                     else:
  211.                         pass
  212.                 else:
  213.                     candidate_class = cls
  214.  
  215.             # if we have a candidate class, carry on checking this and all
  216.             # superclasses for a method annoated as a dbus method
  217.             # on the correct interface
  218.             if (candidate_class and method_name in cls.__dict__
  219.                 and "_dbus_is_method" in cls.__dict__[method_name].__dict__
  220.                 and "_dbus_interface" in cls.__dict__[method_name].__dict__
  221.                 and cls.__dict__[method_name]._dbus_interface == dbus_interface):
  222.                 # the candidate class has a dbus method on the correct interface,
  223.                 # or overrides a method that is, success!
  224.                 parent_method = cls.__dict__[method_name]
  225.                 successful = True
  226.                 break
  227.  
  228.     else:
  229.         # simpler version of above
  230.         for cls in self.__class__.__mro__:
  231.             if (not candidate_class and method_name in cls.__dict__):
  232.                 candidate_class = cls
  233.  
  234.             if (candidate_class and method_name in cls.__dict__
  235.                 and "_dbus_is_method" in cls.__dict__[method_name].__dict__):
  236.                 parent_method = cls.__dict__[method_name]
  237.                 successful = True
  238.                 break
  239.  
  240.     if successful:
  241.         return (candidate_class.__dict__[method_name], parent_method)
  242.     else:
  243.         if dbus_interface:
  244.             raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name, dbus_interface))
  245.         else:
  246.             raise UnknownMethodException('%s is not a valid method' % method_name)
  247.  
  248.  
  249. def _method_reply_return(connection, message, method_name, signature, *retval):
  250.     reply = MethodReturnMessage(message)
  251.     try:
  252.         reply.append(signature=signature, *retval)
  253.     except Exception, e:
  254.         logging.basicConfig()
  255.         if signature is None:
  256.             try:
  257.                 signature = reply.guess_signature(retval) + ' (guessed)'
  258.             except Exception, e:
  259.                 _logger.error('Unable to guess signature for arguments %r: '
  260.                               '%s: %s', retval, e.__class__, e)
  261.                 raise
  262.         _logger.error('Unable to append %r to message with signature %s: '
  263.                       '%s: %s', retval, signature, e.__class__, e)
  264.         raise
  265.  
  266.     connection.send_message(reply)
  267.  
  268.  
  269. def _method_reply_error(connection, message, exception):
  270.     name = getattr(exception, '_dbus_error_name', None)
  271.  
  272.     if name is not None:
  273.         pass
  274.     elif getattr(exception, '__module__', '') in ('', '__main__'):
  275.         name = 'org.freedesktop.DBus.Python.%s' % exception.__class__.__name__
  276.     else:
  277.         name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__)
  278.  
  279.     et, ev, etb = sys.exc_info()
  280.     if isinstance(exception, DBusException) and not exception.include_traceback:
  281.         # We don't actually want the traceback anyway
  282.         contents = exception.get_dbus_message()
  283.     elif ev is exception:
  284.         # The exception was actually thrown, so we can get a traceback
  285.         contents = ''.join(traceback.format_exception(et, ev, etb))
  286.     else:
  287.         # We don't have any traceback for it, e.g.
  288.         #   async_err_cb(MyException('Failed to badger the mushroom'))
  289.         # see also https://bugs.freedesktop.org/show_bug.cgi?id=12403
  290.         contents = ''.join(traceback.format_exception_only(exception.__class__,
  291.             exception))
  292.     reply = ErrorMessage(message, name, contents)
  293.  
  294.     connection.send_message(reply)
  295.  
  296.  
  297. class InterfaceType(type):
  298.     def __init__(cls, name, bases, dct):
  299.         # these attributes are shared between all instances of the Interface
  300.         # object, so this has to be a dictionary that maps class names to
  301.         # the per-class introspection/interface data
  302.         class_table = getattr(cls, '_dbus_class_table', {})
  303.         cls._dbus_class_table = class_table
  304.         interface_table = class_table[cls.__module__ + '.' + name] = {}
  305.  
  306.         # merge all the name -> method tables for all the interfaces
  307.         # implemented by our base classes into our own
  308.         for b in bases:
  309.             base_name = b.__module__ + '.' + b.__name__
  310.             if getattr(b, '_dbus_class_table', False):
  311.                 for (interface, method_table) in class_table[base_name].iteritems():
  312.                     our_method_table = interface_table.setdefault(interface, {})
  313.                     our_method_table.update(method_table)
  314.  
  315.         # add in all the name -> method entries for our own methods/signals
  316.         for func in dct.values():
  317.             if getattr(func, '_dbus_interface', False):
  318.                 method_table = interface_table.setdefault(func._dbus_interface, {})
  319.                 method_table[func.__name__] = func
  320.  
  321.         super(InterfaceType, cls).__init__(name, bases, dct)
  322.  
  323.     # methods are different to signals, so we have two functions... :)
  324.     def _reflect_on_method(cls, func):
  325.         args = func._dbus_args
  326.  
  327.         if func._dbus_in_signature:
  328.             # convert signature into a tuple so length refers to number of
  329.             # types, not number of characters. the length is checked by
  330.             # the decorator to make sure it matches the length of args.
  331.             in_sig = tuple(Signature(func._dbus_in_signature))
  332.         else:
  333.             # magic iterator which returns as many v's as we need
  334.             in_sig = _VariantSignature()
  335.  
  336.         if func._dbus_out_signature:
  337.             out_sig = Signature(func._dbus_out_signature)
  338.         else:
  339.             # its tempting to default to Signature('v'), but
  340.             # for methods that return nothing, providing incorrect
  341.             # introspection data is worse than providing none at all
  342.             out_sig = []
  343.  
  344.         reflection_data = '    <method name="%s">\n' % (func.__name__)
  345.         for pair in zip(in_sig, args):
  346.             reflection_data += '      <arg direction="in"  type="%s" name="%s" />\n' % pair
  347.         for type in out_sig:
  348.             reflection_data += '      <arg direction="out" type="%s" />\n' % type
  349.         reflection_data += '    </method>\n'
  350.  
  351.         return reflection_data
  352.  
  353.     def _reflect_on_signal(cls, func):
  354.         args = func._dbus_args
  355.  
  356.         if func._dbus_signature:
  357.             # convert signature into a tuple so length refers to number of
  358.             # types, not number of characters
  359.             sig = tuple(Signature(func._dbus_signature))
  360.         else:
  361.             # magic iterator which returns as many v's as we need
  362.             sig = _VariantSignature()
  363.  
  364.         reflection_data = '    <signal name="%s">\n' % (func.__name__)
  365.         for pair in zip(sig, args):
  366.             reflection_data = reflection_data + '      <arg type="%s" name="%s" />\n' % pair
  367.         reflection_data = reflection_data + '    </signal>\n'
  368.  
  369.         return reflection_data
  370.  
  371. class Interface(object):
  372.     __metaclass__ = InterfaceType
  373.  
  374. #: A unique object used as the value of Object._object_path and
  375. #: Object._connection if it's actually in more than one place
  376. _MANY = object()
  377.  
  378. class Object(Interface):
  379.     r"""A base class for exporting your own Objects across the Bus.
  380.  
  381.     Just inherit from Object and mark exported methods with the
  382.     @\ `dbus.service.method` or @\ `dbus.service.signal` decorator.
  383.  
  384.     Example::
  385.  
  386.         class Example(dbus.service.object):
  387.             def __init__(self, object_path):
  388.                 dbus.service.Object.__init__(self, dbus.SessionBus(), path)
  389.                 self._last_input = None
  390.  
  391.             @dbus.service.method(interface='com.example.Sample',
  392.                                  in_signature='v', out_signature='s')
  393.             def StringifyVariant(self, var):
  394.                 self.LastInputChanged(var)      # emits the signal
  395.                 return str(var)
  396.  
  397.             @dbus.service.signal(interface='com.example.Sample',
  398.                                  signature='v')
  399.             def LastInputChanged(self, var):
  400.                 # run just before the signal is actually emitted
  401.                 # just put "pass" if nothing should happen
  402.                 self._last_input = var
  403.  
  404.             @dbus.service.method(interface='com.example.Sample',
  405.                                  in_signature='', out_signature='v')
  406.             def GetLastInput(self):
  407.                 return self._last_input
  408.     """
  409.  
  410.     #: If True, this object can be made available at more than one object path.
  411.     #: If True but `SUPPORTS_MULTIPLE_CONNECTIONS` is False, the object may
  412.     #: handle more than one object path, but they must all be on the same
  413.     #: connection.
  414.     SUPPORTS_MULTIPLE_OBJECT_PATHS = False
  415.  
  416.     #: If True, this object can be made available on more than one connection.
  417.     #: If True but `SUPPORTS_MULTIPLE_OBJECT_PATHS` is False, the object must
  418.     #: have the same object path on all its connections.
  419.     SUPPORTS_MULTIPLE_CONNECTIONS = False
  420.  
  421.     def __init__(self, conn=None, object_path=None, bus_name=None):
  422.         """Constructor. Either conn or bus_name is required; object_path
  423.         is also required.
  424.  
  425.         :Parameters:
  426.             `conn` : dbus.connection.Connection or None
  427.                 The connection on which to export this object.
  428.  
  429.                 If None, use the Bus associated with the given ``bus_name``.
  430.                 If there is no ``bus_name`` either, the object is not
  431.                 initially available on any Connection.
  432.  
  433.                 For backwards compatibility, if an instance of
  434.                 dbus.service.BusName is passed as the first parameter,
  435.                 this is equivalent to passing its associated Bus as
  436.                 ``conn``, and passing the BusName itself as ``bus_name``.
  437.  
  438.             `object_path` : str or None
  439.                 A D-Bus object path at which to make this Object available
  440.                 immediately. If this is not None, a `conn` or `bus_name` must
  441.                 also be provided.
  442.  
  443.             `bus_name` : dbus.service.BusName or None
  444.                 Represents a well-known name claimed by this process. A
  445.                 reference to the BusName object will be held by this
  446.                 Object, preventing the name from being released during this
  447.                 Object's lifetime (unless it's released manually).
  448.         """
  449.         if object_path is not None:
  450.             validate_object_path(object_path)
  451.  
  452.         if isinstance(conn, BusName):
  453.             # someone's using the old API; don't gratuitously break them
  454.             bus_name = conn
  455.             conn = bus_name.get_bus()
  456.         elif conn is None:
  457.             if bus_name is not None:
  458.                 # someone's using the old API but naming arguments, probably
  459.                 conn = bus_name.get_bus()
  460.  
  461.         #: Either an object path, None or _MANY
  462.         self._object_path = None
  463.         #: Either a dbus.connection.Connection, None or _MANY
  464.         self._connection = None
  465.         #: A list of tuples (Connection, object path, False) where the False
  466.         #: is for future expansion (to support fallback paths)
  467.         self._locations = []
  468.         #: Lock protecting `_locations`, `_connection` and `_object_path`
  469.         self._locations_lock = thread.allocate_lock()
  470.  
  471.         #: True if this is a fallback object handling a whole subtree.
  472.         self._fallback = False
  473.  
  474.         self._name = bus_name
  475.  
  476.         if conn is None and object_path is not None:
  477.             raise TypeError('If object_path is given, either conn or bus_name '
  478.                             'is required')
  479.         if conn is not None and object_path is not None:
  480.             self.add_to_connection(conn, object_path)
  481.  
  482.     @property
  483.     def __dbus_object_path__(self):
  484.         """The object-path at which this object is available.
  485.         Access raises AttributeError if there is no object path, or more than
  486.         one object path.
  487.  
  488.         Changed in 0.82.0: AttributeError can be raised.
  489.         """
  490.         if self._object_path is _MANY:
  491.             raise AttributeError('Object %r has more than one object path: '
  492.                                  'use Object.locations instead' % self)
  493.         elif self._object_path is None:
  494.             raise AttributeError('Object %r has no object path yet' % self)
  495.         else:
  496.             return self._object_path
  497.  
  498.     @property
  499.     def connection(self):
  500.         """The Connection on which this object is available.
  501.         Access raises AttributeError if there is no Connection, or more than
  502.         one Connection.
  503.  
  504.         Changed in 0.82.0: AttributeError can be raised.
  505.         """
  506.         if self._connection is _MANY:
  507.             raise AttributeError('Object %r is on more than one Connection: '
  508.                                  'use Object.locations instead' % self)
  509.         elif self._connection is None:
  510.             raise AttributeError('Object %r has no Connection yet' % self)
  511.         else:
  512.             return self._connection
  513.  
  514.     @property
  515.     def locations(self):
  516.         """An iterable over tuples representing locations at which this
  517.         object is available.
  518.  
  519.         Each tuple has at least two items, but may have more in future
  520.         versions of dbus-python, so do not rely on their exact length.
  521.         The first two items are the dbus.connection.Connection and the object
  522.         path.
  523.  
  524.         :Since: 0.82.0
  525.         """
  526.         return iter(self._locations)
  527.  
  528.     def add_to_connection(self, connection, path):
  529.         """Make this object accessible via the given D-Bus connection and
  530.         object path.
  531.  
  532.         :Parameters:
  533.             `connection` : dbus.connection.Connection
  534.                 Export the object on this connection. If the class attribute
  535.                 SUPPORTS_MULTIPLE_CONNECTIONS is False (default), this object
  536.                 can only be made available on one connection; if the class
  537.                 attribute is set True by a subclass, the object can be made
  538.                 available on more than one connection.
  539.  
  540.             `path` : dbus.ObjectPath or other str
  541.                 Place the object at this object path. If the class attribute
  542.                 SUPPORTS_MULTIPLE_OBJECT_PATHS is False (default), this object
  543.                 can only be made available at one object path; if the class
  544.                 attribute is set True by a subclass, the object can be made
  545.                 available with more than one object path.
  546.  
  547.         :Raises ValueError: if the object's class attributes do not allow the
  548.             object to be exported in the desired way.
  549.         :Since: 0.82.0
  550.         """
  551.         if path == LOCAL_PATH:
  552.             raise ValueError('Objects may not be exported on the reserved '
  553.                              'path %s' % LOCAL_PATH)
  554.  
  555.         self._locations_lock.acquire()
  556.         try:
  557.             if (self._connection is not None and
  558.                 self._connection is not connection and
  559.                 not self.SUPPORTS_MULTIPLE_CONNECTIONS):
  560.                 raise ValueError('%r is already exported on '
  561.                                  'connection %r' % (self, self._connection))
  562.  
  563.             if (self._object_path is not None and
  564.                 not self.SUPPORTS_MULTIPLE_OBJECT_PATHS and
  565.                 self._object_path != path):
  566.                 raise ValueError('%r is already exported at object '
  567.                                  'path %s' % (self, self._object_path))
  568.  
  569.             connection._register_object_path(path, self._message_cb,
  570.                                              self._unregister_cb,
  571.                                              self._fallback)
  572.  
  573.             if self._connection is None:
  574.                 self._connection = connection
  575.             elif self._connection is not connection:
  576.                 self._connection = _MANY
  577.  
  578.             if self._object_path is None:
  579.                 self._object_path = path
  580.             elif self._object_path != path:
  581.                 self._object_path = _MANY
  582.  
  583.             self._locations.append((connection, path, self._fallback))
  584.         finally:
  585.             self._locations_lock.release()
  586.  
  587.     def remove_from_connection(self, connection=None, path=None):
  588.         """Make this object inaccessible via the given D-Bus connection
  589.         and object path. If no connection or path is specified,
  590.         the object ceases to be accessible via any connection or path.
  591.  
  592.         :Parameters:
  593.             `connection` : dbus.connection.Connection or None
  594.                 Only remove the object from this Connection. If None,
  595.                 remove from all Connections on which it's exported.
  596.             `path` : dbus.ObjectPath or other str, or None
  597.                 Only remove the object from this object path. If None,
  598.                 remove from all object paths.
  599.         :Raises LookupError:
  600.             if the object was not exported on the requested connection
  601.             or path, or (if both are None) was not exported at all.
  602.         :Since: 0.81.1
  603.         """
  604.         self._locations_lock.acquire()
  605.         try:
  606.             if self._object_path is None or self._connection is None:
  607.                 raise LookupError('%r is not exported' % self)
  608.  
  609.             if connection is not None or path is not None:
  610.                 dropped = []
  611.                 for location in self._locations:
  612.                     if ((connection is None or location[0] is connection) and
  613.                         (path is None or location[1] == path)):
  614.                         dropped.append(location)
  615.             else:
  616.                 dropped = self._locations
  617.                 self._locations = []
  618.  
  619.             if not dropped:
  620.                 raise LookupError('%r is not exported at a location matching '
  621.                                   '(%r,%r)' % (self, connection, path))
  622.  
  623.             for location in dropped:
  624.                 try:
  625.                     location[0]._unregister_object_path(location[1])
  626.                 except LookupError:
  627.                     pass
  628.                 if self._locations:
  629.                     try:
  630.                         self._locations.remove(location)
  631.                     except ValueError:
  632.                         pass
  633.         finally:
  634.             self._locations_lock.release()
  635.  
  636.     def _unregister_cb(self, connection):
  637.         # there's not really enough information to do anything useful here
  638.         _logger.info('Unregistering exported object %r from some path '
  639.                      'on %r', self, connection)
  640.  
  641.     def _message_cb(self, connection, message):
  642.         if not isinstance(message, MethodCallMessage):
  643.             return
  644.  
  645.         try:
  646.             # lookup candidate method and parent method
  647.             method_name = message.get_member()
  648.             interface_name = message.get_interface()
  649.             (candidate_method, parent_method) = _method_lookup(self, method_name, interface_name)
  650.  
  651.             # set up method call parameters
  652.             args = message.get_args_list(**parent_method._dbus_get_args_options)
  653.             keywords = {}
  654.  
  655.             if parent_method._dbus_out_signature is not None:
  656.                 signature = Signature(parent_method._dbus_out_signature)
  657.             else:
  658.                 signature = None
  659.  
  660.             # set up async callback functions
  661.             if parent_method._dbus_async_callbacks:
  662.                 (return_callback, error_callback) = parent_method._dbus_async_callbacks
  663.                 keywords[return_callback] = lambda *retval: _method_reply_return(connection, message, method_name, signature, *retval)
  664.                 keywords[error_callback] = lambda exception: _method_reply_error(connection, message, exception)
  665.  
  666.             # include the sender etc. if desired
  667.             if parent_method._dbus_sender_keyword:
  668.                 keywords[parent_method._dbus_sender_keyword] = message.get_sender()
  669.             if parent_method._dbus_path_keyword:
  670.                 keywords[parent_method._dbus_path_keyword] = message.get_path()
  671.             if parent_method._dbus_rel_path_keyword:
  672.                 path = message.get_path()
  673.                 rel_path = path
  674.                 for exp in self._locations:
  675.                     # pathological case: if we're exported in two places,
  676.                     # one of which is a subtree of the other, then pick the
  677.                     # subtree by preference (i.e. minimize the length of
  678.                     # rel_path)
  679.                     if exp[0] is connection:
  680.                         if path == exp[1]:
  681.                             rel_path = '/'
  682.                             break
  683.                         if exp[1] == '/':
  684.                             # we already have rel_path == path at the beginning
  685.                             continue
  686.                         if path.startswith(exp[1] + '/'):
  687.                             # yes we're in this exported subtree
  688.                             suffix = path[len(exp[1]):]
  689.                             if len(suffix) < len(rel_path):
  690.                                 rel_path = suffix
  691.                 rel_path = ObjectPath(rel_path)
  692.                 keywords[parent_method._dbus_rel_path_keyword] = rel_path
  693.  
  694.             if parent_method._dbus_destination_keyword:
  695.                 keywords[parent_method._dbus_destination_keyword] = message.get_destination()
  696.             if parent_method._dbus_message_keyword:
  697.                 keywords[parent_method._dbus_message_keyword] = message
  698.             if parent_method._dbus_connection_keyword:
  699.                 keywords[parent_method._dbus_connection_keyword] = connection
  700.  
  701.             # call method
  702.             retval = candidate_method(self, *args, **keywords)
  703.  
  704.             # we're done - the method has got callback functions to reply with
  705.             if parent_method._dbus_async_callbacks:
  706.                 return
  707.  
  708.             # otherwise we send the return values in a reply. if we have a
  709.             # signature, use it to turn the return value into a tuple as
  710.             # appropriate
  711.             if signature is not None:
  712.                 signature_tuple = tuple(signature)
  713.                 # if we have zero or one return values we want make a tuple
  714.                 # for the _method_reply_return function, otherwise we need
  715.                 # to check we're passing it a sequence
  716.                 if len(signature_tuple) == 0:
  717.                     if retval == None:
  718.                         retval = ()
  719.                     else:
  720.                         raise TypeError('%s has an empty output signature but did not return None' %
  721.                             method_name)
  722.                 elif len(signature_tuple) == 1:
  723.                     retval = (retval,)
  724.                 else:
  725.                     if operator.isSequenceType(retval):
  726.                         # multi-value signature, multi-value return... proceed unchanged
  727.                         pass
  728.                     else:
  729.                         raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
  730.                             (method_name, signature))
  731.  
  732.             # no signature, so just turn the return into a tuple and send it as normal
  733.             else:
  734.                 if retval is None:
  735.                     retval = ()
  736.                 elif (isinstance(retval, tuple)
  737.                       and not isinstance(retval, Struct)):
  738.                 # If the return is a tuple that is not a Struct, we use it
  739.                 # as-is on the assumption that there are multiple return
  740.                 # values - this is the usual Python idiom. (fd.o #10174)
  741.                     pass
  742.                 else:
  743.                     retval = (retval,)
  744.  
  745.             _method_reply_return(connection, message, method_name, signature, *retval)
  746.         except Exception, exception:
  747.             # send error reply
  748.             _method_reply_error(connection, message, exception)
  749.  
  750.     @method(INTROSPECTABLE_IFACE, in_signature='', out_signature='s',
  751.             path_keyword='object_path', connection_keyword='connection')
  752.     def Introspect(self, object_path, connection):
  753.         """Return a string of XML encoding this object's supported interfaces,
  754.         methods and signals.
  755.         """
  756.         reflection_data = _dbus_bindings.DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
  757.         reflection_data += '<node name="%s">\n' % object_path
  758.  
  759.         interfaces = self._dbus_class_table[self.__class__.__module__ + '.' + self.__class__.__name__]
  760.         for (name, funcs) in interfaces.iteritems():
  761.             reflection_data += '  <interface name="%s">\n' % (name)
  762.  
  763.             for func in funcs.values():
  764.                 if getattr(func, '_dbus_is_method', False):
  765.                     reflection_data += self.__class__._reflect_on_method(func)
  766.                 elif getattr(func, '_dbus_is_signal', False):
  767.                     reflection_data += self.__class__._reflect_on_signal(func)
  768.  
  769.             reflection_data += '  </interface>\n'
  770.  
  771.         for name in connection.list_exported_child_objects(object_path):
  772.             reflection_data += '  <node name="%s"/>\n' % name
  773.  
  774.         reflection_data += '</node>\n'
  775.  
  776.         return reflection_data
  777.  
  778.     def __repr__(self):
  779.         where = ''
  780.         if (self._object_path is not _MANY
  781.             and self._object_path is not None):
  782.             where = ' at %s' % self._object_path
  783.         return '<%s.%s%s at %#x>' % (self.__class__.__module__,
  784.                                    self.__class__.__name__, where,
  785.                                    id(self))
  786.     __str__ = __repr__
  787.  
  788. class FallbackObject(Object):
  789.     """An object that implements an entire subtree of the object-path
  790.     tree.
  791.  
  792.     :Since: 0.82.0
  793.     """
  794.  
  795.     SUPPORTS_MULTIPLE_OBJECT_PATHS = True
  796.  
  797.     def __init__(self, conn=None, object_path=None):
  798.         """Constructor.
  799.  
  800.         Note that the superclass' ``bus_name`` __init__ argument is not
  801.         supported here.
  802.  
  803.         :Parameters:
  804.             `conn` : dbus.connection.Connection or None
  805.                 The connection on which to export this object. If this is not
  806.                 None, an `object_path` must also be provided.
  807.  
  808.                 If None, the object is not initially available on any
  809.                 Connection.
  810.  
  811.             `object_path` : str or None
  812.                 A D-Bus object path at which to make this Object available
  813.                 immediately. If this is not None, a `conn` must also be
  814.                 provided.
  815.  
  816.                 This object will implements all object-paths in the subtree
  817.                 starting at this object-path, except where a more specific
  818.                 object has been added.
  819.         """
  820.         super(FallbackObject, self).__init__()
  821.         self._fallback = True
  822.  
  823.         if conn is None:
  824.             if object_path is not None:
  825.                 raise TypeError('If object_path is given, conn is required')
  826.         elif object_path is None:
  827.             raise TypeError('If conn is given, object_path is required')
  828.         else:
  829.             self.add_to_connection(conn, object_path)
  830.